#include "I_UnixNet.h"
#include "I_UnixNetVConnect.h"
#include <zlib/lib/Ink_sock.h>

#define SOCK_BACKLOG  1024

int
PollDescriptor::get_epollfd()
{
	return epoll_fd;
}

uint32_t
PollDescriptor::get_ev_events(int index)
{
	assert(index < POLL_DESCRIPTOR_SIZE);
	return epoll_Trigger_Events[index].events;
}  

void *
PollDescriptor::get_ev_data(int index)
{
	assert(index < POLL_DESCRIPTOR_SIZE);
	return epoll_Trigger_Events[index].data.ptr;
}   

PollDescriptor::PollDescriptor()
{
	result = 0;
	epoll_fd = epoll_create(POLL_DESCRIPTOR_SIZE);
	if(epoll_fd == -1)
		assert(!"the epoll fd create faild!");
	memset(epoll_Trigger_Events, 0, POLL_DESCRIPTOR_SIZE * sizeof(struct epoll_event));
}

int
EventIO::start(PollDescriptor *pd, UnixNetVConnect *vc, uint32_t e)
{
	type = EVENT_IO_READWRITE;
	return	start(pd, vc->fd, (Continuation *)vc, e);
}

int
EventIO::start(PollDescriptor *pd, int afd, Continuation *c, uint32_t e)
{
	cont = c;
	event_loop = pd;
	fd = afd;
	events = e;

	struct epoll_event ev;
	memset(&ev, 0, sizeof(struct epoll_event));
	ev.events = e;
	ev.data.ptr = this;

	return epoll_ctl(event_loop->get_epollfd(), EPOLL_CTL_ADD, fd, &ev); 
}

int
EventIO::modify(uint32_t e, bool add)
{
	assert(event_loop != NULL);
	struct epoll_event ev;
	memset(&ev, 0, sizeof(struct epoll_event));

	uint32_t new_events = events, old_events = events;
	if(!add) //remove
		new_events &= (~e);
	else
		new_events |= e;
	events = new_events;
	ev.events = events;
	ev.data.ptr = this;

	//delete fd, not events
	if(!new_events)
		return epoll_ctl(event_loop->get_epollfd(), EPOLL_CTL_DEL, fd, &ev);
	else if(!old_events)
		return epoll_ctl(event_loop->get_epollfd(), EPOLL_CTL_ADD, fd, &ev);
	else
		return epoll_ctl(event_loop->get_epollfd(), EPOLL_CTL_MOD, fd, &ev);
}

int
EventIO::stop()
{
	assert(event_loop != NULL);
	struct epoll_event ev;
	memset(&ev, 0, sizeof(struct epoll_event));
	ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
	return epoll_ctl(event_loop->get_epollfd(), EPOLL_CTL_DEL, fd, &ev);
}

PollPoller::PollPoller(int p_timeout) : poll_timeout(p_timeout)
{
	pd = new PollDescriptor;
}

void
PollPoller::pollEvent(int wait_timeout)
{
	int timeout;
	if(wait_timeout > poll_timeout)
		timeout = poll_timeout;
	else
		timeout = wait_timeout;

	pd->result = epoll_wait(pd->epoll_fd, pd->epoll_Trigger_Events, POLL_DESCRIPTOR_SIZE, timeout);	
}

NetOptions::NetOptions()
{
	_family = AF_INET;
	_sock_type = SOCK_STREAM;
	_bind_addr = false;
	fd_blocking  = false;
	fd_blocking_connect = false;
}

Connection::Connection()
{
	fd = -1;
	is_bind = false;
	is_connected = false;
}

int
Connection::open(NetOptions opt)
{
	assert((opt._family == AF_INET) || (opt._family == AF_INET6));
	assert((opt._sock_type == SOCK_STREAM) || (opt._sock_type == SOCK_DGRAM));
	fd = ink_socket(opt._family, opt._sock_type, 0);
	if(opt._bind_addr && opt.local.is_valid())
	{
		ink_bind(fd, opt.local.get_addr(), IpEntry::is_ipv4(opt.local.get_addr())
			? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
		is_bind = true;
	}

	return fd;
}

int
Connection::connect(IpEntry *ip_entry, NetOptions opt)
{
	if(ip_entry != NULL)
		_remote_entry = *ip_entry;

	if(!_remote_entry.is_valid())
		return -1;

	//set sockfd nonblocking
	if(!opt.fd_blocking_connect)
	{
		ink_set_nonblock(fd);
	}
	//set fd CLOEXEC
	ink_set_close_exec(fd);

	int res = ::connect(fd, _remote_entry.get_addr(), IpEntry::is_ipv4(_remote_entry.get_addr())
		? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
	if(res == -1 && (opt.fd_blocking_connect || !(EINPROGRESS == errno || EWOULDBLOCK == errno))){
		close();
		return -errno;
	}

	if(opt.fd_blocking)
		ink_set_blocking(fd);
	else
		ink_set_nonblock(fd);

	is_connected = true;
	return 0;
}

void
Connection::close()
{
	::close(fd);
}

AcceptOptions::AcceptOptions()
{
	blocking_accept = true;
	fd_blocking = false;
	sock_type = SOCK_STREAM;
}

Server::Server() : Connection()
{
	is_listen = true;
}

int
Server::listen(const IpEntry *abind_entry, int sock_type, bool fd_blocking_accept)
{
	assert((sock_type == SOCK_STREAM) || (sock_type = SOCK_DGRAM));
	//check bind_entry.
	if(!abind_entry->is_valid())
	{
		return -1;
	}

	bind_entry = *abind_entry;
	int family = IpEntry::is_ipv4(bind_entry.get_addr()) ? AF_INET : AF_INET6;
	//socket
	fd = ink_socket(family, sock_type, 0);
	//set sockopt
	int sockopt_on = 1;
	ink_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &sockopt_on, sizeof(int));
	//bind
	ink_bind(fd, bind_entry.get_addr(), IpEntry::is_ipv4(bind_entry.get_addr()) ?
		sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
	//listen
	ink_listen(fd, SOCK_BACKLOG);
	is_listen = true;
	if(fd_blocking_accept)
		ink_set_blocking(fd);
	else
		ink_set_nonblock(fd);
	//set fd CLOEXEC
	ink_set_close_exec(fd);

	return fd;
}

int
Server::accept(Connection *conn, bool fd_blocking)
{
	assert(is_listen);
	struct sockaddr sa;
	socklen_t sa_len = sizeof(sa);
	int ret = ::accept(fd, &sa, &sa_len);
	if(ret < 0 && !(errno == EAGAIN || errno == EWOULDBLOCK))
	{
		close();
		return -errno;
	}else if(ret < 0)
	{
		return 0;
	}

	int connfd = ret;
	if(fd_blocking)
		ink_set_blocking(connfd);
	else
		ink_set_nonblock(connfd);
	//set fd CLOEXEC
	ink_set_close_exec(connfd);

	conn->fd = connfd;
	conn->_remote_entry.init(&sa);

	return conn->fd;
}
